package puzzle.hashikake.loader;

import java.util.Arrays;

import puzzle.common.loader.AbstractLoader;
import puzzle.common.loader.Preprocessor;
import puzzle.common.point.Direction;
import puzzle.common.point.Pnt;
import puzzle.hashikake.data.HashiDef;
import puzzle.hashikake.data.HashikakeBoard;
import puzzle.hashikake.data.ShimaDef;

public class HashikakeLoader extends AbstractLoader<HashikakeBoard> {

	@Override
	protected HashikakeBoard process(Preprocessor pp) {
		HashikakeBoard b = new HashikakeBoard(pp.getWidth(), pp.getHeight());

		for (int y = 0; y < b.height; y++) {
			String[] fields = pp.getLines().get(y);
			for (int x = 0; x < fields.length; x++) {
				int c = fields[x].charAt(0);
				if (c >= '1' && c <= '8') {
					b.addShima(x, y, c - '0');
				}
			}
		}

		setupHashi(b);

		return b;
	}

	private void setupHashi(HashikakeBoard b) {
		int[] hashiIndexArray = new int[b.width * b.height];
		Arrays.fill(hashiIndexArray, -1);

		for (int y = 0; y < b.height; y++) {
			for (int x = 0; x < b.width; x++) {
				ShimaDef s = b.getShima(x, y);
				if (s != null) {
					findHashiFrom(b, s, Direction.E, hashiIndexArray);
					findHashiFrom(b, s, Direction.S, hashiIndexArray);
				}
			}
		}
	}

	private void findHashiFrom(HashikakeBoard b, ShimaDef s1, Direction d, int[] hashiIndexArray) {
		Pnt p = s1.p;
		ShimaDef s2 = null;
		while (true) {
			p = p.add(d.v);
			if (!b.inRange(p)) {
				break;
			}
			s2 = b.getShima(p);
			if (s2 != null) {
				break;
			}
		}

		if (s2 != null) {
			HashiDef hashi = b.addHashi(s1, s2);
			s1.addHashi(hashi);
			s2.addHashi(hashi);
			findCrossing(b, s1, s2, d, hashi, hashiIndexArray);
		}
	}

	private void findCrossing(HashikakeBoard b, ShimaDef s1, ShimaDef s2, Direction d,
			HashiDef hashi1, int[] hashiIndexArray) {
		for (Pnt p = s1.p.add(d.v); !p.equals(s2.p); p = p.add(d.v)) {
			assert b.inRange(p);
			int i = b.getLinearIndex(p.x, p.y);
			if (hashiIndexArray[i] >= 0) {
				HashiDef hashi2 = b.getHashi(hashiIndexArray[i]);
				hashi1.addCrossing(hashi2);
				hashi2.addCrossing(hashi1);
			} else {
				hashiIndexArray[i] = hashi1.i;
			}
		}
	}

}
